﻿using System.Collections.Generic;
using System.Text;
using DatabaseGroup.SSIS.SourceAdapters.Parser;

namespace DatabaseGroup.SSIS.SourceAdapters.Model
{
    /// <summary>
    /// A model for accessing and configuring the underlying 
    /// source file access
    /// </summary>
    public class SourceFileModel : ISourceFileModel
    {
        /// <summary>
        /// Gets or sets the full file name of the source file
        /// </summary>
        public string FileName { get; set; }

        /// <summary>
        /// Gets or sets the encoding used for reading file data
        /// </summary>
        public Encoding Encoding { get; set; }

        /// <summary>
        /// Gets or sets the source file column delimiter
        /// </summary>
        public string ColumnDelimiter { get; set; }

        /// <summary>
        /// Gets or sets the source file row delimiter
        /// </summary>
        public string RowDelimiter { get; set; }

        /// <summary>
        /// Gets or sets if the first data row contains the column names
        /// </summary>
        public bool ColumnNamesInFirstRow { get; set; }

        /// <summary>
        /// Gets or sets the number of initial data rows to skip
        /// before reading data
        /// </summary>
        public int DataRowsToSkip { get; set; }

        /// <summary>
        /// Gets or sets the header row delimiter
        /// </summary>
        public string HeaderRowDelimiter { get; set; }

        /// <summary>
        /// Gets or sets the number of initial header rows to skip before
        /// reading data rows
        /// </summary>
        public int HeaderRowsToSkip { get; set; }

        /// <summary>
        /// Gets or sets the text qualifier to use for encapsulating column data
        /// </summary>
        public string TextQualifier { get; set; }

        /// <summary>
        /// Gets a number of rows from the source file
        /// </summary>
        /// <param name="offset">The number of initial data rows to skip</param>
        /// <param name="limit">The maximum number of rows to return</param>
        /// <param name="skipAllInitialRows">
        /// True if all initial header/data rows should be skipped, 
        /// false if only initial header rows should be skipped
        /// </param>
        /// <remarks>
        /// Hides any errors when reading file data because at this
        /// stage we don't care about file errors - only during execution
        /// </remarks>
        /// <returns>The list of rows, each containing a list of column data</returns>
        public List<string[]> GetRows(int offset, int limit, bool skipAllInitialRows)
        {
            FileReader reader = CreateReader();
            DelimitedFileParser parser = CreateParser();
            List<string[]> rows = new List<string[]>();

            try
            {
                // TODO: rename/refactor; ambiguous bool name
                if (skipAllInitialRows)
                    parser.SkipInitialRows(reader);
                else
                    parser.SkipHeaderRows(reader);
            }
            catch
            {
                reader.Close();
                return rows;
            }

            try
            {
                RowData rowData = new RowData();

                for (int rowsSkipped = 0; rowsSkipped < offset; rowsSkipped++)
                    parser.ParseNextRow(reader, rowData);

                for (int rowsRead = 0; rowsRead < limit; rowsRead++)
                {
                    if (reader.IsEOF)
                        return rows;

                    parser.ParseNextRow(reader, rowData);
                    string[] columns = new string[rowData.ColumnCount];

                    for (int colIndex = 0; colIndex < rowData.ColumnCount; colIndex++)
                    {
                        columns[colIndex] = rowData.GetColumnData(colIndex);
                    }

                    rows.Add(columns);
                }
            }
            catch
            {
                // TODO: add proper file exception handling within the presenters
            }
            finally
            {
                reader.Close();
            }

            return rows;
        }

        /// <summary>
        /// Creates a new FileReader based on the model settings
        /// </summary>
        /// <returns>The initialized file reader</returns>
        private FileReader CreateReader()
        {
            return new FileReader(this.FileName, this.Encoding);
        }

        /// <summary>
        /// Creates a new DelimitedFileParser based on the model settings
        /// </summary>
        /// <returns>The initialized delimited file parser</returns>
        private DelimitedFileParser CreateParser()
        {
            DelimitedFileParser parser =
                new DelimitedFileParser(this.ColumnDelimiter, this.RowDelimiter)
                {
                    ColumnNameInFirstRow = this.ColumnNamesInFirstRow,
                    DataRowsToSkip = this.DataRowsToSkip,
                    HeaderRowDelimiter = this.HeaderRowDelimiter,
                    HeaderRowsToSkip = this.HeaderRowsToSkip,
                    TextQualifier = this.TextQualifier
                };

            return parser;
        }
    }
}
